/** @file terminal.c
 *
 * @author cameris camerisdump@gmail.com
 */

#include "terminal.h"
#include "font.h"
#include "color.h"
#include "lcd.h"
#include "working_timer.h"
#include <string.h>
//TODO remove delay when using interrupt for status
#include <util/delay.h>
#include <avr/interrupt.h>

#define TERM_COLS (LCD_SIZE_X/FONT_WIDTH) /**< @brief columns in the terminal */
#define TERM_LINES (LCD_SIZE_Y/FONT_HEIGHT) /**< @brief lines in the terminal */

#define TERM_LAST_COL (TERM_COLS-1) /**< @brief number of the last column */
#define TERM_LAST_LINE (TERM_LINES-1) /**< @brief number of the last line */

#define TAB_TABLE_SIZE ((TERM_LAST_COL/8) + 1) /**< @brief size of the tab buffer */

struct terminal terminal; /**< @brief current terminal options */

struct charbuf dispbuf[TERM_LINES][TERM_COLS]; /**< @brief character buffer */
struct charbuf (*screen[TERM_LINES])[TERM_COLS]; /**< @brief screen */
uint8_t tabs[TAB_TABLE_SIZE]; /**< @brief tab position table */
/*struct font currentfont; [>*< @brief currently selected font <]*/

/* save/restore cursor */
struct terminal termsave; /**< @brief terminal options save
					 * @see term_save_cursor()
					 * @see term_restore_cursor() */

/** @brief blink counter */
static void term_blink_cnt(void)
{ /* {{{ */
	static uint8_t blinkcnt = 0;
	blinkcnt++;
	if ((TERM_BLINK_MS/WTIMER_MS) <= blinkcnt) {
		terminal.blink_do = 1;
		terminal.blink ^= 1;
		blinkcnt = 0;
	}
} /* }}} */

/** @brief draws character
 * @param x column
 * @param y line */
static void term_draw(uint8_t x, uint8_t y)
{ /* {{{ */
	uint8_t tmpcolor;
	struct charbuf chbuf = (*screen[y])[x];

	if (chbuf.blink && terminal.blink) {
		chbuf.ch = ' ';
		chbuf.font = FONT_C0;
		chbuf.underline = 0;
	}

	if (x == terminal.cx && y == terminal.cy && terminal.drawcursor) {
		/* cursor drawn with inverted standard colors */
		chbuf.fgcolor = color_get_std_bg();
		chbuf.bgcolor = color_get_std_fg();
	}

	if (chbuf.inverse) {
		tmpcolor = chbuf.fgcolor;
		chbuf.fgcolor = chbuf.bgcolor;
		chbuf.bgcolor = tmpcolor;
	}

	font_draw_char(x, y, &chbuf);
} /* }}} */

/** @brief draws a range in the terminal
 *
 * draws the terminal from left to right until the end is reached,
 * includes start and end
 * @param x1 column of the start
 * @param y1 line of the start
 * @param x2 column of the end
 * @param y2 line of the end */
static void term_draw_range(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{ /* {{{ */
	while (1) {
		term_draw(x1, y1);
		if (x1 == x2 && y1 == y2) break;
		if (++x1 > TERM_LAST_COL) {
			x1 = 0;
			y1++;
		}
	}
} /* }}} */

/** @brief hides the cursor from the terminal
 * @return 1 if cursor is already hidden, 0 otherwise */
static uint8_t term_cursor_hide(void)
{ /* {{{ */
	if( !terminal.drawcursor ) return 1;
	terminal.drawcursor = 0;
	term_draw(terminal.cx, terminal.cy);
	return 0;
} /* }}} */

/** @brief shows the cursor on the terminal
 *
 * shows the cursor on the terminal, unless it is disabled
 * by the term_set_cursor_visible() function */
static void term_cursor_show(void)
{ /* {{{ */
	if( !terminal.visiblecursor || terminal.drawcursor ) return;
	terminal.drawcursor = 1;
	term_draw(terminal.cx, terminal.cy);
} /* }}} */

/** @brief erases in the terminal
 *
 * erases the terminal with spaces from left to right until
 * the end is reached, includes start and end
 * @param x1 column of the start
 * @param y1 line of the start
 * @param x2 column of the end
 * @param y2 line of the end */
static void term_erase(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{ /* {{{ */
	struct charbuf tmp;
	uint8_t x = x1;
	uint8_t y = y1;

	tmp = terminal.cb;
	term_reset_char_attr();
	terminal.cb.ch = ' ';
	terminal.cb.font = FONT_C0;

	while (1) {
		(*screen[y])[x] = terminal.cb;
		if (x == x2 && y == y2) break;
		if (++x > TERM_LAST_COL) {
			x = 0;
			y++;
		}
	}
	term_draw_range(x1, y1, x2, y2);
	terminal.cb = tmp;
} /* }}} */

/** @brief resets the terminal */
void term_reset(void)
{ /* {{{ */

	term_reset_char_attr();

	terminal.cb.fgcolor = color_get_std_fg();
	terminal.cb.bgcolor = color_get_std_bg();

	terminal.insert = 0;
	terminal.origin = 0;
	terminal.autowrap = 0;

	font_reset();

	term_set_margins(0,TERM_LAST_LINE);
	
	term_set_cursor_visible(1);

	term_tab_clear(3);
} /* }}} */

/** @brief initializes the terminal */
void term_init(void)
{ /* {{{ */
	for (uint8_t y=0; y<TERM_LINES; y++) {
		screen[y] = &dispbuf[TERM_LAST_LINE-y];
	}
	wtimer_add(&term_blink_cnt);

	term_set_cursor_position(0,0);
	term_set_local_echo(0);
	term_set_show_control_chars(0);
	term_set_newlinemode(0);
	term_set_screenmode(0);
	term_set_cursorkeymode(0);
	term_set_backarrowmode(0);
	term_set_kp_application(0);
	term_tab_clear(3);

	term_reset();

	termsave = terminal;

	term_erase_display(2);
} /* }}} */

/** @brief resets all character attributes */
void term_reset_char_attr(void)
{ /* {{{ */
	terminal.cb.underline = 0;
	terminal.cb.inverse = 0;
	terminal.cb.bold = 0;
	terminal.cb.blink = 0;
} /* }}} */

/** @brief set a tabstop
 * @param x the column to set the tabstop */
void term_tab_set(uint_fast8_t x)
{
	uint8_t i = x/8;
	tabs[i] |= (1<<(7-(x%8)));
}

/** @brief clear a tabstop
 * @param mode 0 clears tabstop at current position, 3 clears all */
void term_tab_clear(uint_fast8_t mode)
{ /* {{{ */
	uint8_t i;
	switch (mode) {
		case 0:
			i = terminal.cx/8;
			tabs[i] &= ~(1<<(7-(terminal.cx%8)));
			break;
		case 3:
			for (i=0; i < TAB_TABLE_SIZE; i++) {
				tabs[i] = 0;
			}
			break;
	}
} /* }}} */

/** @brief move to a tabstop
 * @param n nth tabstop, negative values move to the previous tabstop */
void term_tab_move(int_fast8_t n)
{ /* {{{ */
	uint_fast8_t x = terminal.cx;

	if (n == 0) return;

	term_cursor_hide();

	while(n != 0) {
		if ( ( tabs[x/8] & (1<< (7-(x%8))) ) != 0 ) {
			/* ignore tab at current position */
			if(x != terminal.cx) {
				if (n > 0) n--;
				else n++;
				terminal.cx = x;
				continue;
			}
		}
		if (0 == x) {
			terminal.cx = 0;
			break;
		} else if (TERM_LAST_COL == x) {
			terminal.cx = TERM_LAST_COL;
			break;
		}
		if (n > 0) x++;
		else x--;
	}

	term_cursor_show();
} /* }}} */

/** @brief redraw  blink characters if needed */
void term_redraw_blink(void)
{ /* {{{ */
	if (terminal.blink_do) {
		terminal.blink_do = 0;
		for (uint_fast8_t y=0; y<TERM_LINES; y++) {
			for(uint_fast8_t x=0; x<TERM_COLS; x++) {
				if((*screen[y])[x].blink == 1) {
					/* ignore blank spaces */
					if ( !(0x20 == (*screen[y])[x].ch &&
						FONT_C0 == (*screen[y])[x].font &&
						0 == (*screen[y])[x].underline) )
					{
						term_draw(x,y);
					}
				}
			}
		}
	}
} /* }}} */

/** @brief scrolls the term inside the margins
 * @param n lines to scroll, positive values scroll up, negative values scroll down
 * @see term_move_cursor() */
void term_scroll(int_fast8_t n)
{ /* {{{ */
	int_fast8_t sign = 1;
	uint_fast8_t lines;
	struct charbuf (*tmp)[TERM_COLS];

	if (0 == n) return;
	else if (n < 0) sign = -1;

	lines = terminal.mbottom -terminal.mtop +1;

	if ((sign*n) > lines) { /* erase all inside margins  */
		term_erase(0, terminal.mtop, TERM_LAST_COL, terminal.mbottom);
	} else if (sign > 0) { /* scroll up */
		for (uint_fast8_t i=terminal.mtop; i<=(terminal.mbottom-n); i++) {
			tmp = screen[i];
			screen[i] = screen[i+n];
			screen[i+n] = tmp;
		}
		term_draw_range(0, terminal.mtop, TERM_LAST_COL, (terminal.mbottom-n));
		term_erase(0, (terminal.mbottom-n+1), TERM_LAST_COL, terminal.mbottom);
	} else { /* scroll down */
		for (uint_fast8_t i=terminal.mbottom; i>=(terminal.mtop-n); i--) {
			tmp = screen[i];
			screen[i] = screen[i+n];
			screen[i+n] = tmp;
		}
		term_draw_range(0, (terminal.mtop-n), TERM_LAST_COL, terminal.mbottom);
		term_erase(0, terminal.mtop, TERM_LAST_COL, (terminal.mtop-n-1));
	}

} /* }}} */

/** @brief move the cursor relative from current position
 * @param dx columns to move back or forward
 * @param dy lines to move up or down
 * @return number of lines it could not move past a line margin, so it can
 *         be used in combination with term_scroll() */
int_fast8_t term_move_cursor(int_fast8_t dx, int_fast8_t dy)
{ /* {{{ */
	int_fast16_t scrolllines = 0;
	uint_fast8_t stopline; 
	uint_fast8_t bw_flag = 0;
	uint_fast8_t x = terminal.cx, y = terminal.cy;

	term_cursor_hide();

	if (dx > 0) {
		for (uint_fast8_t i=0; i<dx; i++) {
			if (TERM_LAST_COL == x) break;
			x++;
		}
	} else if (dx < 0) {
		for (uint_fast8_t i=0; i<(-dx); i++) {
			if (x <= 0) {
				/* only backwars movement wraps around left margin, see bw in
				 * terminfo */
				if (terminal.autowrap) {
					bw_flag = 1;
					x = TERM_LAST_COL;
					dy--;
				} else {
					x = 0;
					break;
				}
			} else {
				x--;
			}
		}
	}

	if (dy > 0) {
		if (y > terminal.mbottom) {
			stopline = TERM_LAST_LINE;
		} else {
			stopline = terminal.mbottom;
			scrolllines = dy - stopline + y;
			if (scrolllines < 0) scrolllines = 0;
		}
		for (uint_fast8_t i=0; i<dy; i++) {
			if (y >= stopline) break;
			y++;
		}
	} else if (dy < 0) {
		if (y < terminal.mtop) {
			stopline = 0;
		} else {
			stopline = terminal.mtop;
			scrolllines = dy - stopline + y;
			if (scrolllines > 0) scrolllines = 0;
		}
		for (uint_fast8_t i=0; i<(-dy); i++) {
			if (y <= stopline) {
				y = stopline;
				if (bw_flag) x = 0;
				break;
			}
			y--;
		}
	}
	terminal.cx = x;
	terminal.cy = y;
		
	term_cursor_show();

	return scrolllines;
} /* }}} */

/** @brief move the cursor to absolute positon
 * @param x column to move
 * @param y line to move */
void term_set_cursor_position(uint_fast8_t x, uint_fast8_t y)
{ /* {{{ */
	term_cursor_hide();

	if (x > TERM_LAST_COL) x = TERM_LAST_COL;
	if (y > TERM_LAST_LINE) y = TERM_LAST_LINE;

	terminal.cx = x;
	terminal.cy = y;

	term_cursor_show();
} /* }}} */

/** @brief get the current line of the cursor
 * @return current line of the cursor */
uint8_t term_get_cursor_line(void)
{
	return terminal.cy;
}

/** @brief get the current column of the cursor
 * @return current column of the cursor */
uint8_t term_get_cursor_col(void)
{
	return terminal.cx;
}

/** @brief set margins
 * @param top line of the top margin
 * @param bottom line of the bottom margin */
void term_set_margins(uint_fast8_t top, uint_fast8_t bottom)
{ /* {{{ */
	if (bottom > TERM_LAST_LINE) bottom = TERM_LAST_LINE;
	if (top > bottom) {
		top = 0;
		bottom = TERM_LAST_LINE;
	}

	terminal.mtop = top;
	terminal.mbottom = bottom;
} /* }}} */

/** @brief delete characters at cursor position
 *
 * delete characters, moves characters right of the cursor to the left.
 * only works inside margins. does not move the cursor.
 * @param n number of characters */
void term_del_char(uint_fast8_t n)
{ /* {{{ */
	if (n == 0 || terminal.cy < terminal.mtop || terminal.cy > terminal.mbottom) return;

	if (n <= TERM_LAST_COL-terminal.cx) {
		memmove(&(*screen[terminal.cy])[terminal.cx], &(*screen[terminal.cy])[terminal.cx+n],
				sizeof(struct charbuf)*(TERM_LAST_COL-terminal.cx-n+1));
		term_draw_range(terminal.cx, terminal.cy, TERM_LAST_COL-n, terminal.cy);
	}
	term_erase(TERM_LAST_COL-n+1, terminal.cy, TERM_LAST_COL, terminal.cy);
} /* }}} */

/** @brief insert spaces at cursor position
 *
 * insert spaces at cursor position and moves characters to the right.
 * characters moved past the right margin are lost. only works inside
 * margins. does not move the cursor.
 * @param n number of characters */
void term_ins_char(uint_fast8_t n)
{ /* {{{ */
	if (n == 0 || terminal.cy < terminal.mtop || terminal.cy > terminal.mbottom) return;

	if (n <= TERM_LAST_COL-terminal.cx) {
		memmove(&(*screen[terminal.cy])[terminal.cx+n], &(*screen[terminal.cy])[terminal.cx],
				sizeof(struct charbuf)*(TERM_LAST_COL-terminal.cx-n+1));
		term_draw_range(terminal.cx+n, terminal.cy, TERM_LAST_COL, terminal.cy);
	}
	term_erase(terminal.cx, terminal.cy, terminal.cx+n-1, terminal.cy);
} /* }}} */

/** @brief overwrite characters with spaces from cursor position to the right
 * @param n number of characters */
void term_erase_char(uint_fast8_t n)
{ /* {{{ */
	if (n == 0) return;

	n--;
	if (terminal.cx+n > TERM_LAST_COL) n = TERM_LAST_COL-terminal.cx;

	term_erase(terminal.cx, terminal.cy, terminal.cx+n, terminal.cy);
} /* }}} */

/** @brief delete or insert lines, only works inside margins.
 * @param n number of lines to delete, if negative.
 *          number of lines to insert, if positive.*/
void term_ins_or_del_line(int_fast8_t n)
{ /* {{{ */
	uint_fast8_t tmp_mtop;
	int_fast8_t sign = -1; /* negative sign inserts lines */

	if (n == 0 || terminal.cy < terminal.mtop || terminal.cy > terminal.mbottom) return;

	if (n < 0) {
		sign = 1; /* positive sign deletes lines */
		n *= -1;
	}

	if (n > terminal.mbottom-terminal.cy+1) n = terminal.mbottom-terminal.cy+1;

	tmp_mtop = terminal.mtop;

	term_set_margins(terminal.cy, terminal.mbottom);
	term_scroll( sign * n );
	term_set_margins(tmp_mtop, terminal.mbottom);
} /* }}} */

/** @brief deletes inside line, including cursor
 * @param erasemode
 *        + 0 from cursor to end of line,
 *        + 1 from beginning of line to the cursor,
 *        + 2 entire line */
void term_erase_line(uint_fast8_t erasemode)
{ /* {{{ */
	uint_fast8_t x1, x2;

	switch (erasemode) {
		case 0: // erase from cursor to end of line
			x1 = terminal.cx;
			x2 = TERM_LAST_COL;
			break;
		case 1: // erase from beginning of line to cursor
			x1 = 0;
			x2 = terminal.cx;
			break;
		case 2: // erase entire line
			x1 = 0;
			x2 = TERM_LAST_COL;
			break;
		default:
			return;
	}

	term_erase(x1, terminal.cy, x2, terminal.cy);
} /* }}} */

/** @brief deletes inside display, including cursor
 * @param erasemode
 *        + 0 from cursor to end of display,
 *        + 1 from beginning of display to the cursor,
 *        + 2 entire display */
void term_erase_display(uint_fast8_t erasemode)
{ /* {{{ */
	uint_fast8_t y1, y2;

	switch (erasemode) {
		case 0: // erase from cursor to end of screen
			term_erase_line(erasemode);
			y1 = terminal.cy+1;
			y2 = TERM_LAST_LINE;
			if (y1 > y2) y1 = y2;
			break;
		case 1: // erase from beginning of screen to cursor
			term_erase_line(erasemode);
			y1 = 0;
			y2 = terminal.cy-1;
			if (y2 < y1) y2 = y1;
			break;
		case 2: // erase entire screen
			y1 = 0;
			y2 = TERM_LAST_LINE;
			break;
		default:
			return;
	}

	term_erase(0, y1, TERM_LAST_COL, y2);
} /* }}} */

/** @brief saves the cursor position, currently active character and terminal options */
void term_save_cursor(void)
{
	termsave = terminal;
	font_save_state();
}

/** @brief restores the cursor position, character options and
 * autowrap and origin option */
void term_restore_cursor(void)
{ /* {{{ */
	term_cursor_hide();

	font_restore_state();
	terminal.autowrap = termsave.autowrap;
	terminal.origin = termsave.origin;

	terminal.cb = termsave.cb;

	term_set_cursor_position(termsave.cx, termsave.cy);

	term_cursor_show();
} /* }}} */

/** @brief print a character with the currently active character options to terminal
 * @see terminal.cb
 * @param ch character to print */
void term_print (uint8_t ch)
{ /* {{{ */
	uint8_t curfont;

	if (0x80 > ch) { /* C0 and GL */
		curfont = font_get_font(font_get_selected(GL));
		if (0x00 <= ch && 0x1F >= ch) { /* C0 control character */
			terminal.cb.font = FONT_C0;
		} else {
			terminal.cb.font = curfont;
		}
	} else { /* C1 and GR */
		curfont = font_get_font(font_get_selected(GR));
		if (0x80 <= ch && 0x9F >= ch) { /* C1 control character */
			terminal.cb.font = FONT_C1;
		} else {
			terminal.cb.font = curfont;
		}
		ch -= 128;
	}

	/* 94 char sets */
	if ((font_get_setwidth(curfont) < 96) && (0x20 == ch || 0x7F == ch)) {
		terminal.cb.font = FONT_C0;
		if (0x7F == ch && !terminal.showcontrol) return; /* ignore DEL */
	}

	terminal.cb.ch = ch;

	// move when in insertion mode
	if (terminal.insert) {
		// if not last char in line, move chars in line
		if (terminal.cx < TERM_LAST_COL) {
			memmove(&(*screen[terminal.cy])[terminal.cx+1], &(*screen[terminal.cy])[terminal.cx],
					sizeof(struct charbuf)*(TERM_LAST_COL-terminal.cx));
			term_draw_range(terminal.cx+1, terminal.cy, TERM_LAST_COL, terminal.cy);
		}
	}

	(*screen[terminal.cy])[terminal.cx] = terminal.cb;

	// if cursor already hidden, we have to redraw to update the changed char
	if ( term_cursor_hide() ) {
		term_draw(terminal.cx, terminal.cy);
	}

	if (++terminal.cx > TERM_LAST_COL) {
		if (terminal.autowrap) {
			term_scroll( term_move_cursor(-1*terminal.cx, 1) );
		} else {
			terminal.cx = TERM_LAST_COL;
		}
	}

	term_cursor_show();
} /* }}} */

/** @brief set foreground color
 * @see terminal.cb */
void term_set_fgcolor(uint8_t col)
{
	terminal.cb.fgcolor = col;
}

/** @brief set background color
 * @see terminal.cb */
void term_set_bgcolor(uint8_t col)
{
	terminal.cb.bgcolor = col;
}

/** @brief reset foreground color
 * @see terminal.cb */
void term_reset_fgcolor(void)
{
	terminal.cb.fgcolor = color_get_std_fg();
}

/** @brief reset background color
 * @see terminal.cb */
void term_reset_bgcolor(void)
{
	terminal.cb.bgcolor = color_get_std_bg();
}

/** @brief get current foregound color
 * @return returns the currently used foreground color
 * @see terminal.cb */
uint8_t term_get_fgcolor(void)
{
	return terminal.cb.fgcolor;
}

/** @brief set bold attribute
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal.cb */
void term_set_char_bold(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.cb.bold = b;
}

/** @brief set underline attribute
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal.cb */
void term_set_char_underline(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.cb.underline = b;
}

/** @brief set inverse attribute
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal.cb */
void term_set_char_inverse(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.cb.inverse = b;
}

/** @brief set blink attribute
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal.cb */
void term_set_char_blink(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.cb.blink = b;
}

/** @brief set local echo terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_local_echo(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.localecho = b;
}

/** @brief set autowrap terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_autowrap(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.autowrap = b;
}

/** @brief set insert terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_insertmode(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.insert = b;
}

/** @brief set origin terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_originmode(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.origin = b;
}

/** @brief set newline terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_newlinemode(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.newline = b;
}

/** @brief set show control characters terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_show_control_chars(uint_fast8_t b)
{ /* {{{ */
	if (b != 0) b = 1;
	terminal.showcontrol = b;
	if (b) {
		terminal.autowrap = 1;
	}
} /* }}} */

/** @brief set visible cursor terminal option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_cursor_visible(uint_fast8_t b)
{ /* {{{ */
	if (b != 0) b = 1;
	terminal.visiblecursor = b;
	if (b) term_cursor_show();
	else term_cursor_hide();
} /* }}} */

/** @brief set light or dark background
 * @param b bool 1 = ON, 0 = OFF */
void term_set_screenmode(uint_fast8_t b)
{ /* {{{ */
	if (b) color_set_light_bg();
	else color_set_dark_bg();
	if (terminal.cb.fgcolor == color_get_std_bg()) {
		terminal.cb.fgcolor = color_get_std_fg();
	}
	if (terminal.cb.bgcolor == color_get_std_fg()) {
		terminal.cb.bgcolor = color_get_std_bg();
	}
	for (uint_fast8_t y=0; y<TERM_LINES; y++) {
		for (uint_fast8_t x=0; x<TERM_COLS; x++) {
			if ((*screen[y])[x].fgcolor == color_get_std_bg() ) {
				(*screen[y])[x].fgcolor = color_get_std_fg();
			}
			if ((*screen[y])[x].bgcolor == color_get_std_fg() ) {
				(*screen[y])[x].bgcolor = color_get_std_bg();
			}
		}
	}
	term_draw_range(0, 0, TERM_LAST_COL, TERM_LAST_LINE);
} /* }}} */

/** @brief set the state of keypad application option
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_kp_application(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.kp_app = b;
}

/** @brief set the state of backarrow mode
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_backarrowmode(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.backarrow = b;
}

/** @brief set the state of cursor key mode
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_cursorkeymode(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.cursorkey = b;
}

/** @brief send C1 mode
 * @param b bool 1 = ON, 0 = OFF
 * @see terminal */
void term_set_sendc1(uint_fast8_t b)
{
	if (b != 0) b = 1;
	terminal.sendc1 = b;
}

/** @brief get the index of the last column
 * @return index of last column */
uint8_t term_get_last_col(void)
{
	return TERM_LAST_COL;
}

/** @brief get the index of the last line
 * @return index of last line */
uint8_t term_get_last_line(void)
{
	return TERM_LAST_LINE;
}

/** @brief get the state of show control character option
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint_fast8_t term_get_show_control_chars(void)
{
	return terminal.showcontrol;
}

/** @brief get the state of local echo option
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_local_echo(void)
{
	return terminal.localecho;
}

/** @brief get the state of newline option
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_newlinemode(void)
{
	return terminal.newline;
}

/** @brief get the state of origin option
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_originmode(void)
{
	return terminal.origin;
}

/** @brief get the state of keypad application option
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_kp_application(void)
{
	return terminal.kp_app;
}

/** @brief get the state of backarrow mode
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_backarrowmode(void)
{
	return terminal.backarrow;
}

/** @brief get the state of cursor key mode
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_cursorkeymode(void)
{
	return terminal.cursorkey;
}

/** @brief get the state of send C1 mode
 * @return 1 = ON, 0 = OFF
 * @see terminal */
uint8_t term_get_sendc1(void)
{
	return terminal.sendc1;
}

/** @brief get state of bold attribute
 * @return 1 = ON, 0 = OFF
 * @see terminal.cb */
uint8_t term_get_char_bold(void)
{
	return terminal.cb.bold;
}

/** @brief get the top margin
 * @return currently set top margin */
uint8_t term_get_top_margin(void)
{
	return terminal.mtop;
}

/** @brief get the bottom margin
 * @return currently set bottom margin */
uint8_t term_get_bottom_margin(void)
{
	return terminal.mbottom;
}

/** @brief print a temporary string in the last line
 *
 * print a temporary string in the last line, blocks while displaying. no
 * option is changed by this.
 * @param str string to print */
void term_status_line(uint8_t *str)
{ /* {{{ */
	struct terminal ttmp = terminal;
	struct charbuf (*ptmp)[TERM_COLS];
	struct charbuf ltmp[TERM_COLS];

	/* save last line */
	ptmp = screen[TERM_LAST_LINE];
	screen[TERM_LAST_LINE] = &ltmp;
	/*memcpy(&ltmp[0], (*screen[TERM_LAST_LINE]),*/
			/*sizeof(struct charbuf)*TERM_COLS);*/

	term_set_cursor_visible(0);
	term_set_autowrap(0);
	term_set_cursor_position(0,TERM_LAST_LINE);

	term_reset_char_attr();

	terminal.cb.fgcolor = color_get_std_fg();
	terminal.cb.bgcolor = color_get_std_bg();

	terminal.cb.bold = 1;
	terminal.cb.inverse = 1;

	while (*str) {
		term_print(*str);
		str++;
	}
	if (terminal.cx != TERM_LAST_COL) {
		while (terminal.cx != TERM_LAST_COL) {
			term_print(' ');
		}
		term_print(' ');
	}

	_delay_ms(STATUS_TIME);

	/*memcpy((*screen[TERM_LAST_LINE]), &ltmp[0], */
			/*sizeof(struct charbuf)*TERM_COLS);*/
	screen[TERM_LAST_LINE] = ptmp;
	term_draw_range(0, TERM_LAST_LINE, TERM_LAST_COL, TERM_LAST_LINE);
	terminal = ttmp;
	term_draw(terminal.cx,terminal.cy);
} /* }}} */

/* vim:set foldmethod=marker: */
